


#include "metric.h"
#include <time.h>
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>



#define DV_MIN  0.0
#define DV_MAX  1.79769e+308
#define TV_MIN  0
#define TV_MAX  0xFFFFFFFFFFFFFFFF
#define UV_MIN  0
#define UV_MAX  0xFFFFFFFFFFFFFFFF

#define DV_KB   (1024.0)
#define DV_MB   ((DV_KB) * (DV_KB))
#define DV_GB   ((DV_KB) * (DV_MB))

#define UV_KB   (1024)
#define UV_MB   ((UV_KB) * (UV_KB))
#define UV_GB   ((UV_KB) * (UV_MB))

#define PTS_SEC 1000000
#define PTS_MS  1000

#define PERIOD_TO_PTS(period)   (period * 1000000)
#define PTS_TO_SEC(pts)         (pts / 1000000.0)
#define BYTES_TO_BITS(bytes)    (bytes * 8)
#define BITS_TO_BYTES(bits)     (bits / 8)



static metric_pts metric_get_pts(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (metric_pts)(tv.tv_sec * 1000000 + tv.tv_usec);
}

static void metric_dv_update(metric_dv *dv, metric_dt v)
{
    if (dv->min > v) {
        dv->min = v;
    }

    if (dv->max < v) {
        dv->max = v;
    }

    dv->val = v;
    dv->acc += v;
}

static void metric_tv_update(metric_tv *tv, metric_pts v)
{
    if (tv->min > v) {
        tv->min = v;
    }

    if (tv->max < v) {
        tv->max = v;
    }

    tv->val = v;
    tv->acc += v;
}

static void metric_uv_update(metric_uv *uv, metric_ut v)
{
    if (uv->min > v) {
        uv->min = v;
    }

    if (uv->max < v) {
        uv->max = v;
    }

    uv->val = v;
    uv->acc += v;
}

static void metric_dv_init(metric_dv *dv)
{
    dv->val  = DV_MIN;
    dv->min  = DV_MAX;
    dv->max  = DV_MIN;
    dv->mea  = DV_MIN;
    dv->acc  = DV_MIN;
}

static void metric_tv_init(metric_tv *tv)
{
    tv->val  = TV_MIN;
    tv->min  = TV_MAX;
    tv->max  = TV_MIN;
    tv->mea  = TV_MIN;
    tv->acc  = TV_MIN;
}

static void metric_uv_init(metric_uv *uv)
{
    uv->val  = UV_MIN;
    uv->min  = UV_MAX;
    uv->max  = UV_MIN;
    uv->mea  = UV_MIN;
    uv->acc  = UV_MIN;
}

char *metric_pts_str(metric_pts pts)
{
    static char string[32] = {'\0'};
    if (pts >= PTS_SEC) {
        sprintf(string, "%llu(second)", pts / PTS_SEC);
    } else if (pts >= PTS_MS) {
        sprintf(string, "%llu(ms)", pts / PTS_MS);
    } else {
        sprintf(string, "%llu(us)", pts);
    }

    return string;
}

char *metric_bytes_str(metric_ut bytes)
{
    double val = (double)bytes;
    static char string[32] = {'\0'};
    if (val >= DV_GB) {
        sprintf(string, "%.04f(GB)", val / DV_GB);
    } else if (val >= DV_MB) {
        sprintf(string, "%.04f(MB)", val / DV_MB);
    } else if (val >= DV_KB) {
        sprintf(string, "%.04f(KB)", val / DV_KB);
    } else {
        sprintf(string, "%.04f(B)", val);
    }

    return string;
}

char *metric_bps_str(metric_dt bits)
{
    static char string[32] = {'\0'};
    if (bits >= DV_GB) {
        sprintf(string, "%.04f(Gbps)", bits / DV_GB);
    } else if (bits >= DV_MB) {
        sprintf(string, "%.04f(Mbps)", bits / DV_MB);
    } else if (bits >= DV_KB) {
        sprintf(string, "%.04f(Kbps)", bits / DV_KB);
    } else {
        sprintf(string, "%.04f(bps)", bits);
    }

    return string;
}

void metric_tp_init(metric_tp *metric, metric_sec period_second, 
    metric_tp_fn period_fn)
{
    metric->pts       = 0;
    metric->bytes     = 0;
    metric->count     = 0;
    metric->period    = period_second;
    metric->period_fn = period_fn;
    metric_dv_init(&metric->tp);
}

void metric_tp_update(metric_tp *metric, metric_ut bytes)
{
    metric_dt tp;
    metric_pts pts;

    pts = metric_get_pts();

    metric->count++;
    metric->bytes += bytes;

    if (!metric->pts) {
        metric->pts = pts;
        return;
    }

    if ((pts - metric->pts) < PERIOD_TO_PTS(metric->period)) {
        return;
    }

    metric->pts = pts;

    tp = BYTES_TO_BITS(metric->bytes) / metric->period;
    metric->bytes = 0;

    metric_dv_update(&metric->tp, tp);

    if (metric->period_fn) {
        metric->period_fn(tp);
    }
}

void metric_op_init(metric_op *metric)
{
    metric->start = 0;
    metric->count = 0;
    metric_uv_init(&metric->bytes);
    metric_dv_init(&metric->tp);
    metric_tv_init(&metric->lantacy);
}

void metric_op_start(metric_op *metric)
{
    metric->start = metric_get_pts();
}

void metric_op_stop(metric_op *m, metric_ut bytes)
{
    metric_ut bits;
    metric_dt seconds;
    metric_dt tp;
    metric_pts duration;

    duration = metric_get_pts() - m->start;

    m->count++;

    metric_uv_update(&m->bytes, bytes);
    metric_tv_update(&m->lantacy, duration);

    seconds = PTS_TO_SEC(duration);
    bits    = BYTES_TO_BITS(bytes);
    tp      = bits / seconds;
    metric_dv_update(&m->tp, tp);

    seconds    = PTS_TO_SEC(m->lantacy.acc);
    bits       = BYTES_TO_BITS(m->bytes.acc);
    m->tp.mea = bits / seconds;
}